package com.open.capacity.workflow.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.open.capacity.form.service.FormDbTableService;
import com.open.capacity.workflow.enums.WFTransportTypes;
import com.open.capacity.workflow.model.WFEngine.WFCondition;
import com.open.capacity.workflow.model.WFEngine.WFLineInfo;
import com.open.capacity.workflow.model.WFEngine.WFNodeInfo;
import com.open.capacity.workflow.model.WFEngine.WFSchemeModel;
import com.open.capacity.workflow.model.WFScheme;
import com.open.capacity.workflow.service.WFSchemeExtService;
import com.open.capacity.workflow.vo.WFParameter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;


@Slf4j
@Service
public class WFSchemeExtServiceImpl implements WFSchemeExtService {

    @Autowired
    private FormDbTableService formDbTableService;

    private WFSchemeModel schemeModel;
    private ConcurrentHashMap<String, WFNodeInfo> nodesMap;

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void schemeInit(WFScheme scheme) throws IOException {
        schemeModel = objectMapper.readValue(scheme.getScheme(), WFSchemeModel.class);
        nodesMap = new ConcurrentHashMap<>();
        for (WFNodeInfo node : schemeModel.getNodes()) {
            if (!nodesMap.containsKey(node.getId())) {
                nodesMap.put(node.getId(), node);
            }
        }
    }

    @Override
    public WFNodeInfo getStartNode() {
        WFNodeInfo startNode = null;
        for (WFNodeInfo node : schemeModel.getNodes()) {
            if (node.getType().equals("startround")) {
                startNode = node;
                break;
            }
        }
        return startNode;
    }


    @Override
    public WFNodeInfo getNode(String nodeId) {
        return nodesMap.get(nodeId);
    }

    @Override
    public List<WFNodeInfo> getNextNodes(String nodeId, WFTransportTypes transportType, WFParameter parameter) {
        List<WFNodeInfo> nextNodes = new ArrayList<>();
        for (WFLineInfo line : schemeModel.getLines()) {
            if (line.getFrom().equals(nodeId)) {
                boolean isOk = false;
                switch (transportType) {
                    case Agree:
                        if (line.getWftype() == 1 || line.getWftype() == 4 || line.getWftype() == 6) {
                            isOk = true;
                        }
                        break;
                    case Disagree:
                        if (line.getWftype() == 2 || line.getWftype() == 5 || line.getWftype() == 6) {
                            isOk = true;
                        }
                        break;
                    case Overtime:
                        if (line.getWftype() == 3 || line.getWftype() == 4 || line.getWftype() == 5) {
                            isOk = true;
                        }
                        break;
                }
                if(line.getWftype() == 6) {
                    if(CollectionUtils.isNotEmpty(line.getConditions())) {
                        isOk = false;
                        if(calcConditionLine(line, parameter)) {
                            isOk = true;
                        }
                    }
                }
                if (isOk) {
                    WFNodeInfo nextNode = nodesMap.get(line.getTo());
                    if (nextNode != null) {
                        nextNodes.add(nextNode);
                    }
                }
            }
        }
        return nextNodes;
    }

    /**
     * 条件节点判断
     *
     * @param lineInfo  线条信息
     * @param parameter 参数
     * @return
     */
    private boolean calcConditionLine(WFLineInfo lineInfo, WFParameter parameter) {
        boolean res = false;
        if (lineInfo.getConditions().size() > 0) {
            Map<String, Object> formData = null;
            try {
                formData = objectMapper.readValue(parameter.getFormData(), Map.class);
            } catch (IOException e) {
                e.printStackTrace();
                log.error(e.getMessage());
            }
            for (WFCondition condition : lineInfo.getConditions()) {
                res = false;
                if (formData.get(condition.getFieldId()) != null) {
                    String v1 = formData.get(condition.getFieldId()).toString();
                    switch (condition.getCompareType())//比较类型1.等于2.不等于3.大于4.大于等于5.小于6.小于等于7.包含8.不包含9.包含于10.不包含于
                    {
                        case 1:
                            if (v1.equals(condition.getValue())) {
                                res = true;
                            }
                            break;
                        case 2:
                            if (v1 != condition.getValue()) {
                                res = true;
                            }
                            break;
                        case 3:
                            if (new BigDecimal(v1).compareTo(new BigDecimal(condition.getValue())) > 0) {
                                res = true;
                            }
                            break;
                        case 4:
                            if (new BigDecimal(v1).compareTo(new BigDecimal(condition.getValue())) >= 0) {
                                res = true;
                            }
                            break;
                        case 5:
                            if (new BigDecimal(v1).compareTo(new BigDecimal(condition.getValue())) < 0) {
                                res = true;
                            }
                            break;
                        case 6:
                            if (new BigDecimal(v1).compareTo(new BigDecimal(condition.getValue())) <= 0) {
                                res = true;
                            }
                            break;
                        case 7:
                            if (v1.contains(condition.getValue())) {
                                res = true;
                            }
                            break;
                        case 8:
                            if (!v1.contains(condition.getValue())) {
                                res = true;
                            }
                            break;
                        case 9:
                            if (condition.getValue().contains(v1)) {
                                res = true;
                            }
                            break;
                        case 10:
                            if (!condition.getValue().contains(v1)) {
                                res = true;
                            }
                            break;
                    }
                }
            }
        } else if (StringUtils.isNotEmpty(lineInfo.getConditionSql())) {
            String conditionSql = lineInfo.getConditionSql().replace("{processId}", "#{processId}");
            Map<String, Object> params = new HashMap<>();
            params.put("processId", parameter.getProcessId());
            List<Map<String, Object>> result = formDbTableService.findData(lineInfo.getDbConditionId(), conditionSql, params);
            if (result.size() > 0) {
                res = true;
            }
        } else {
            res = true;
        }
        return res;
    }

    @Override
    public Integer getPreNodeNum(String nodeId) {
        int num = 0;
        for (WFLineInfo line : schemeModel.getLines()) {
            if (line.getTo().equals(nodeId)) {
                num++;
            }
        }
        return num;
    }

    @Override
    public Boolean isToNode(String fromNodeId, String toNodeId) {
        boolean res = false;
        for (WFLineInfo line : schemeModel.getLines()) {
            if (line.getFrom().equals(fromNodeId)) {
                if (line.getTo().equals(toNodeId)) {
                    res = true;
                    break;
                } else {
                    if (line.getTo().equals(fromNodeId) || nodesMap.get(line.getTo()) == null || nodesMap.get(line.getTo()).getType().equals("endround")) {
                        break;
                    } else if (isToNode(line.getTo(), toNodeId)) {
                        res = true;
                        break;
                    }
                }
            }

        }
        return res;
    }
}
